﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Input;

using DepSoft.Helpers.iTunes;
using DepSoft.UI.WPF.iWatchSyncer.Properties;
using System.Windows.Controls;

namespace DepSoft.UI.WPF.iWatchSyncer
{
    /// <summary>
    /// Interaction logic for iWatchWindow.xaml
    /// </summary>
    public partial class iWatchWindow : Window
    {
        #region Private members
        private iTunesWrapper                   _iTunesWrapper;
        private NotifyIcon                      _iWatchSyncerTrayIcon           = new NotifyIcon();
        private ContextMenuStrip                _iWatchSyncerContextMenuStrip   = new ContextMenuStrip { Name = "iWatchSyncerContextMenuStrip", ShowImageMargin = false };
        private FileSystemWatcher               _iWatcherFolder                 = new FileSystemWatcher();
        private FileSystemWatcher               _iWatcherMp3                    = new FileSystemWatcher();
        private BlockingQueue<string>           _iWatchList                     = new BlockingQueue<string>();
        private List<string>                    _iWatchFileList                 = new List<string>();
        private Thread                          _iWatchiTunesJobConsumerThread;
        private object                          _locker                         = new object();
        private DateTime                        _lastCreatedEvent               = DateTime.Now;
        private System.Threading.Timer          _timeOutTimer;
        #endregion

        #region Constructor
        public iWatchWindow()
        {
            InitializeComponent();

            this.txtTitle.Text = "iWatch Syncer";
            this.InitializeNotifyIcon();

            this.InitializeiTunes();

            this.InitializeFileSystemWatcher();
            this.InitializeFormControls();
            this.InitializeThreads();

            CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, new ExecutedRoutedEventHandler(delegate(object sender, ExecutedRoutedEventArgs args){this.Close();})));
        }
        #endregion

        #region Private methods
        #region Form initialization
        private void InitializeNotifyIcon()
        {
            ToolStripMenuItem toolStripMenuItemClose = new ToolStripMenuItem { Name = "toolStripMenuItemClose", Text = "Close" };
            toolStripMenuItemClose.Click += new System.EventHandler(ToolStripMenuItemClose_Click);
            _iWatchSyncerContextMenuStrip.Items.Add(toolStripMenuItemClose);

            _iWatchSyncerTrayIcon.Text = "iWatchSyncer";
            _iWatchSyncerTrayIcon.Icon = new Icon(Settings.Default.iWatchIcon);
            _iWatchSyncerTrayIcon.ContextMenuStrip = _iWatchSyncerContextMenuStrip;
            _iWatchSyncerTrayIcon.Visible = true;
            _iWatchSyncerTrayIcon.DoubleClick += new EventHandler(iWatchSyncerTrayIcon_DoubleClick);
        }

        private void InitializeFormControls()
        {
            this.txtiWatchDirectory.Text = Settings.Default.iWatchDirectory;
            //this.txtiWatchPlayList.Text = Settings.Default.iWatchPlayList;
            this.checkBoxiWatchPlayList.IsChecked = Settings.Default.iWatchPlayListUse;

            List<string> playLists = _iTunesWrapper.GetPlayLists();
            if(!playLists.Contains(Constants.PLAYLIST_IWATCHSYNCER))
                playLists.Add(Constants.PLAYLIST_IWATCHSYNCER);

            foreach (string playList in playLists)
            {
                ComboBoxItem cboItem = new ComboBoxItem(){ Content = playList };
                this.cboiWatchPlayList.Items.Add(cboItem);
            }

            if(playLists.Contains(Settings.Default.iWatchPlayList))
                this.cboiWatchPlayList.Text = Settings.Default.iWatchPlayList;

            this.uriPayPal.NavigateUri = new Uri(@"https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2EX59N6GY6L92&lc=BE&item_name=DepSoft&item_number=iWatchSyncer&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHostedGuest");
        }

        private void InitializeiTunes()
        {
            if (ReferenceEquals(_iTunesWrapper, null))
            {
                lock (_locker)
                {
                    if (ReferenceEquals(_iTunesWrapper, null))
                    {
                        //Start up iTunes
                        _iTunesWrapper = new iTunesWrapper();
                        //Hook up Quitting Event
                        _iTunesWrapper.iTunesQuitting += new EventHandler(iTunesWrapper_iTunesQuitting);
                    }
                }
            }
        }

        private void InitializeFileSystemWatcher()
        {
            if(!string.IsNullOrEmpty(Settings.Default.iWatchDirectory))
            {
                try
                {
                    if (!Directory.Exists(Settings.Default.iWatchDirectory))
                        Directory.CreateDirectory(Settings.Default.iWatchDirectory);
                }
                catch
                {
                    //If there is any error while creating the directory currently set, just clear it!
                    Settings.Default.iWatchDirectory = string.Empty;
                    Settings.Default.Save();
                }

                _iWatcherFolder.Path = Settings.Default.iWatchDirectory;
                _iWatcherMp3.Path = Settings.Default.iWatchDirectory;
            }

            //Only check for additions of mp3 files
            //_iWatcher.Filter = "*.mp3";
            //_iWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.DirectoryName | NotifyFilters.FileName;

            //Only watch for folder creations
            _iWatcherFolder.NotifyFilter = NotifyFilters.DirectoryName;
            _iWatcherFolder.InternalBufferSize = 32768;
            _iWatcherFolder.IncludeSubdirectories = true;

            //Only watch for mp3 file additions
            _iWatcherMp3.Filter = string.Concat("*", Constants.TRACK_EXTENSION_MP3);
            _iWatcherMp3.InternalBufferSize = 32768;
            _iWatcherMp3.IncludeSubdirectories = true;
        }

        private void InitializeThreads()
        {
            _iWatchiTunesJobConsumerThread = new Thread(this.ConsumeiWatchListItems);
            _iWatchiTunesJobConsumerThread.IsBackground = true;
            _iWatchiTunesJobConsumerThread.Start();

            //Set up the Timer to verify the last received FileSystemWatcher event
            TimerCallback timerCallBackDelegate = new TimerCallback(TimerTimeOut);
            _timeOutTimer = new System.Threading.Timer(timerCallBackDelegate, null, 0, 3500);
        }
        #endregion

        #region Timer methods
        private void TimerTimeOut(Object stateInfo)
        {
            lock (_locker)
            {
                if (DateTime.Now.Subtract(_lastCreatedEvent).Duration().TotalSeconds > 7 && _iWatchFileList.Count > 0)
                {
                    lock (_iWatchFileList)
                    {
                        foreach (string path in _iWatchFileList)
                        {
                            ////When we encounter only a directory we need to add all tracks from this directory and subdirectories
                            //if (!Path.HasExtension(path) || !Path.GetExtension(path).Equals(Constants.TRACK_EXTENSION, StringComparison.OrdinalIgnoreCase))
                            //{
                            //    foreach (string file in Directory.GetFiles(path, string.Concat("*", Constants.TRACK_EXTENSION), SearchOption.AllDirectories))
                            //        _iWatchList.Enqueue(file);
                            //}
                            ////When we encounter a mp3 file, just add it to the list
                            //else
                                _iWatchList.Enqueue(path);
                        }

                        _iWatchFileList.Clear();
                    }
                }
            }
        }
        #endregion

        #region Threading methods
        private void ConsumeiWatchListItems()
        {
            foreach (string iTunesPath in _iWatchList)
            {
                //If a Halt Queue was issued, break out of the infinite loop
                if (string.IsNullOrEmpty(iTunesPath))
                    break;

                //If we encounter a m3u file, just skip it
                if (Path.GetExtension(iTunesPath).Equals(Constants.TRACK_EXTENSION_M3U, StringComparison.OrdinalIgnoreCase))
                    continue;

                //If we encounter a mp3 file, use the Track import
                if (Path.GetExtension(iTunesPath).Equals(Constants.TRACK_EXTENSION_MP3, StringComparison.OrdinalIgnoreCase))
                {
                    if (Settings.Default.iWatchPlayListUse)
                        _iTunesWrapper.ImportTrackToPlayList(Settings.Default.iWatchPlayList, iTunesPath);
                    else
                        _iTunesWrapper.ImportTrackToiTunes(iTunesPath);
                }
                //If we encounter a path, use the Tracks import
                else
                {
                    if (Settings.Default.iWatchPlayListUse)
                        _iTunesWrapper.ImportTracksToPlayList(Settings.Default.iWatchPlayList, new List<string>() { iTunesPath });
                    else
                        _iTunesWrapper.ImportTracksToiTunes(new List<string>() { iTunesPath });
                }
            }
        }
        #endregion

        #region Event handling - User members
        private void iTunesWrapper_iTunesQuitting(object sender, EventArgs e)
        {
            //The WPF way to perform an invoke on a form
            this.Dispatcher.Invoke((MethodInvoker)delegate() { this.Close(); });
        }
        #endregion

        #region Event handling - Form
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            if (!string.IsNullOrEmpty(Settings.Default.iWatchDirectory))
            {
                this.Visibility = Visibility.Collapsed;
                this.StartFileSystemWatcher();
            }
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            Settings.Default.iWatchPlayListUse = this.checkBoxiWatchPlayList.IsChecked.Value;
            Settings.Default.iWatchPlayList = this.cboiWatchPlayList.Text;
            Settings.Default.Save();

            _iWatchList.HaltQueue();
            if (!ReferenceEquals(_iWatchiTunesJobConsumerThread, null) && _iWatchiTunesJobConsumerThread.IsAlive)
                _iWatchiTunesJobConsumerThread.Join();

            if(!ReferenceEquals(_iTunesWrapper, null))
                _iTunesWrapper.Dispose();

            _iWatchSyncerTrayIcon.Dispose();
        }
        #endregion

        #region Event handling - Form controls
        private void iWatchSyncerTrayIcon_DoubleClick(object sender, EventArgs e)
        {
            if (this.Visibility == Visibility.Collapsed)
            {
                this.ShowInTaskbar = true;
                this.Show();
            }
            else
                this.Visibility = Visibility.Collapsed;
        }

        private void ToolStripMenuItemClose_Click(object sender, System.EventArgs e)
        {
            this.Close();
        }

        private void cmdMinimize_Click(object sender, RoutedEventArgs e)
        {
            this.Visibility = Visibility.Collapsed;
        }

        private void cmdSelect_Click(object sender, RoutedEventArgs e)
        {
            FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
            folderBrowserDialog.SelectedPath = Settings.Default.iWatchDirectory;

            if (folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                this.StopFileSystemWatcher();
                Settings.Default.iWatchDirectory = folderBrowserDialog.SelectedPath;
                Settings.Default.Save();

                this.txtiWatchDirectory.Text = Settings.Default.iWatchDirectory;
                this.InitializeFileSystemWatcher();
                this.StartFileSystemWatcher();
            }
        }

        private void cboiWatchPlayList_DropDownClosed(object sender, EventArgs e)
        {
            Settings.Default.iWatchPlayList = this.cboiWatchPlayList.Text;
            Settings.Default.Save();
        }

        private void checkBoxiWatchPlayList_Click(object sender, RoutedEventArgs e)
        {
            Settings.Default.iWatchPlayListUse = this.checkBoxiWatchPlayList.IsChecked.Value;
            Settings.Default.Save();
        }

        private void HandleRequestNavigate(object sender, RoutedEventArgs e)
        {
            Process.Start(new ProcessStartInfo(((Hyperlink)sender).NavigateUri.ToString()));
            e.Handled = true;
        }
        #endregion

        #region FileSystemWatcher handlers
        private void StopFileSystemWatcher()
        {
            _iWatcherFolder.Created -= new FileSystemEventHandler(OniWatcherCreated);
            _iWatcherFolder.EnableRaisingEvents = false;

            _iWatcherMp3.Created -= new FileSystemEventHandler(OniWatcherCreated);
            _iWatcherMp3.EnableRaisingEvents = false;
        }

        private void StartFileSystemWatcher()
        {
            //if (_iWatchiTunesJobConsumerThread.ThreadState != System.Threading.ThreadState.Running)
            //    _iWatchiTunesJobConsumerThread.Start();

            _iWatcherFolder.Created += new FileSystemEventHandler(OniWatcherCreated);
            _iWatcherFolder.EnableRaisingEvents = true;

            _iWatcherMp3.Created += new FileSystemEventHandler(OniWatcherCreated);
            _iWatcherMp3.EnableRaisingEvents = true;
        }

        /// <summary>
        /// FileSystemWatcher create event handler
        /// Gets called when the FileSystemWatcher detects a file or directory creation
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks>
        /// Following options are possible
        ///     1. A directory gets moved to the watch location
        ///     2. A directory gets copied to the watch location
        ///     3. A file gets moved to the watch location
        ///     4. A file gets copied to the watch location
        ///     
        /// For option 1, we will only get the directory creation notification, so we add the full path
        /// For option 2, we will get the directory creation notification and also the file creation notifications!
        /// For option 3 and 4, we will only get a file creation notification
        /// 
        /// Option 2 is difficult to keep track... but we handle this by only storing the directory creation full path in the watchlist and omit all other creations that reside in this directory or any subdirectory!
        /// </remarks>
        private void OniWatcherCreated(object sender, FileSystemEventArgs e)
        {
            lock (_iWatchFileList)
            {
                _lastCreatedEvent = DateTime.Now;

                if (Path.HasExtension(e.FullPath) && Path.GetExtension(e.FullPath).Equals(Constants.TRACK_EXTENSION_MP3, StringComparison.OrdinalIgnoreCase) && !_iWatchFileList.ContainsFolder(Path.GetDirectoryName(e.FullPath)))
                    _iWatchFileList.Add(e.FullPath);
                else if(!_iWatchFileList.ContainsFolder(Path.GetDirectoryName(e.FullPath)))
                    _iWatchFileList.Add(e.FullPath);              
            }
        }
        #endregion
        #endregion
    }
}